/**
 * 
 */
package com.I9lou.se.lurker.index.support;

import java.util.Calendar;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.lucene.index.IndexWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.I9lou.se.lurker.index.IndexOptimizer;
import com.I9lou.se.lurker.index.IndexWriterFactory;

/**
 * 索引优化器实现类，定时执行索引文件优化操作。
 * @author chenwei
 * @date 2011-03-07
 * @ThreadSafe
 */
public class SimpleIndexOptimizer implements IndexOptimizer {
	
	/**
	 * 日志输出
	 */
	private static Logger LOG = LoggerFactory.getLogger(SimpleIndexOptimizer.class);
	
	/**
	 * 定时任务执行框架
	 */
	private ScheduledExecutorService scheduler = null;
	
	/**
	 * IndexWriter实例工厂
	 */
	private IndexWriterFactory indexWriterFactory = null;
	
	/**
	 * 当前正在执行优化的索引目录名称。
	 */
	private AtomicReference<String> currentOptimizeDirectory = new AtomicReference<String>("");
	
	/**
	 * 索引优化任务启动间隔时间为24小时，单位：毫秒
	 */
	private final long period = 24 * 60 * 60 * 1000;
	
	public void setIndexWriterFactory(IndexWriterFactory indexWriterFactory) {
		this.indexWriterFactory = indexWriterFactory;
	}

	@Override
	public boolean isOptimizing(String indexDirName) {
		return currentOptimizeDirectory.get().equals(indexDirName);
	}
	
	@Override
	public boolean isOptimizing(int appId) {
		String indexDirName = currentOptimizeDirectory.get();
		if( indexDirName.length() == 0 ) {
			return false;
		} else {
			return Integer.valueOf(indexDirName.split("-")[0]) == appId;
		}
	}

	/**
	 * 索引优化器启动函数，通常在服务运行前调用且仅调用一次
	 */
	public void init() {
		long startTime = System.currentTimeMillis(); //设置索引第一次优化时间为第二天的凌晨1点，之后每隔24小时运行一次
		Calendar firstOptimizeTime = Calendar.getInstance();
		firstOptimizeTime.set(Calendar.DAY_OF_MONTH, firstOptimizeTime.get(Calendar.DAY_OF_MONTH) + 1);
		firstOptimizeTime.set(Calendar.HOUR_OF_DAY, 1);
		scheduler = Executors.newSingleThreadScheduledExecutor();
		scheduler.scheduleAtFixedRate(new OptimizeTask(), 
									  firstOptimizeTime.getTimeInMillis() - System.currentTimeMillis(), 
									  period, TimeUnit.MILLISECONDS);
		LOG.info( "Init simpleIndexOptimizer success. cost: {}ms. First optimize at: {}",
				  System.currentTimeMillis() - startTime,
				  firstOptimizeTime.getTime() );
	}
	
	public void destroy() {
		long startTime = System.currentTimeMillis();
		scheduler.shutdown();
		LOG.info( "Shutdown simpleIndexOptimizer success. cost: {}ms",
				  System.currentTimeMillis() - startTime );
	}
	
	/**
	 * 索引优化执行任务，由定时调度器调度执行
	 * @author chenwei
	 */
	private class OptimizeTask implements Runnable {
		
		@Override
		public void run() {
			long startTime = System.currentTimeMillis();
			try {
				for( String indexDirName : indexWriterFactory.getIndexDirNames() ) {
					currentOptimizeDirectory.set(indexDirName);
					try {
						IndexWriter writer = indexWriterFactory.get(indexDirName);
						if( writer != null ) {
							long singleIndexStartTime = System.currentTimeMillis();
							writer.optimize();
							writer.commit();
							LOG.info( "{} optimize success! cost: {}ms", indexDirName,
									  System.currentTimeMillis()-singleIndexStartTime );
						} else {
							LOG.warn( "{} optimize failed! Can't get IndexWriter instance!", indexDirName);
						}
					} catch (OutOfMemoryError e) {
						LOG.error( "{} optimize failed! IndexWriter will be closed! {}", 
								   indexDirName, e );
						indexWriterFactory.close(indexDirName);
					} catch(Exception e) {
						LOG.error( indexDirName + " optimize failed!", e );
					}
				}
			} catch(Exception e) {
				LOG.error("Encount unexcepted exception while optimizing!", e);
			}
			currentOptimizeDirectory.set("");
			LOG.info( "OptimizeTask execute over! cost: {}ms", 
					  System.currentTimeMillis()-startTime );
		}
		
	}

}
